//
//  EzOnvifServer.cpp
//  ffmpeg_onvif
//
//  Created by 刘跃龙 on 8/9/18.
//  Copyright © 2018 absolutemobile. All rights reserved.
//

#include "EzOnvifServer.hpp"
#include "EzMulticastServer.h"
#include "EzFileSystem.h"
#include "tinyxml.h"
#include "EzXml.hpp"

#include <stdio.h>
#include <iostream>

#include <arpa/inet.h> //inet_ntoa
#include <unistd.h> //gethostname
#include <netdb.h> //gethostbyname

#include "openssl/ssl.h"
#include "EzBase64.h"
#include "EzText.h"
#include "ABMUtils.h"
#include "AccountData.h"
#include "onvif_template.h"
EZ_BEGIN

EzOnvifServer* EzOnvifServer::instance = NULL;
EzOnvifServer* EzOnvifServer::getInstance(){
    if(!(EzOnvifServer::instance)){
        EzOnvifServer::instance = new EzOnvifServer(8080);
        Ez::EzOnvifServer::Profile profile;
        EzOnvifServer::instance->addProfile(profile);
    }
    return EzOnvifServer::instance;
}

std::string EzOnvifServer::getLocalIP() {
    //std::string string1 = Ez::getValueBySystemCommand("ifconfig eth0 | grep \"RUNNING\"");
    std::string szIpaddr;
    //if (string1.size() > 0) { // 网线
    //    szIpaddr = Ez::getValueBySystemCommand("ifconfig eth0 | grep \"inet\"");
    if ((access("/sys/class/net/eth0/carrier", F_OK)) != -1) {
	std::string value = Ez::getValueBySystemCommand("cat /sys/class/net/eth0/carrier");
	int size0 = value.find("0");
	int size1 = value.find("1");
	if (size1 >= 0) {
	    szIpaddr = Ez::getValueBySystemCommand("ifconfig eth0 | grep \"inet\"");
	} else if(size0 >= 0) {	    
	    szIpaddr = Ez::getValueBySystemCommand("ifconfig ra0 | grep \"inet\"");
	}
    } else { // wifi
        szIpaddr = Ez::getValueBySystemCommand("ifconfig ra0 | grep \"inet\"");
    }
    
    size_t ipSize = szIpaddr.find("addr:");
    if (ipSize >= szIpaddr.size()) {
    }else {
        size_t bCastSize = szIpaddr.find("Bcast:");
        if (bCastSize >= szIpaddr.size()) {
        }else {
            if (bCastSize > 5 && szIpaddr.size() > bCastSize) {
                std::string ipAddr = szIpaddr.substr(ipSize + 5, bCastSize - ipSize - 5);
                log("**ip:%s---\n", ipAddr.c_str());
                return ABMUtils::trim(ipAddr);
            }
        }
        
    }
    return "";
}

std::string EzOnvifServer::getUUID()
{
    char guid[37];
    const char *c = "89ab";
    char *p = guid;
    int n;
    for( n = 0; n < 16; ++n )
    {
        int b = rand()%255;
        switch( n )
        {
            case 6:
                sprintf(p, "4%x", b%15 );
                break;
            case 8:
                sprintf(p, "%c%x", c[rand()%strlen(c)], b%15 );
                break;
            default:
                sprintf(p, "%02x", b);
                break;
        }
        
        p += 2;
        switch( n )
        {
            case 3:
            case 5:
            case 7:
            case 9:
                *p++ = '-';
                break;
        }
    }
    *p = 0;
    return guid;
}

EzOnvifServer::EzOnvifServer(unsigned short int port,std::string uuid,std::string path,std::string localIP)
:m_port(port)
,m_path(path)
,m_lastBuffer("")
{
    if(uuid.empty()){
        m_uuid = Ez::EzOnvifServer::getUUID();
    }else{
        m_uuid = uuid;
    }
}

void EzOnvifServer::start()
{

}

void EzOnvifServer::stop()
{
}

EzOnvifServer::~EzOnvifServer()
{
    stop();
}

std::string EzOnvifServer::getResProbeMatches(std::string messageId,std::string type)
{
    std::string res = "";
    std::string resTemplate;
    if(type == "Device"){
        resTemplate =Ez::base64Decode(FILE_ProbeMatchesDevice);
//        resTemplate =Ez::readStringFromFile("./onvif_template/ProbeMatchesDevice.xml");
    }else{
        resTemplate =Ez::base64Decode(FILE_ProbeMatchesNetworkVideoTransmitter);
//        resTemplate =Ez::readStringFromFile("./onvif_template/ProbeMatchesNetworkVideoTransmitter.xml");
    }
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pMessageID = EzXml::getChildElement(&doc,"Envelope","Header","MessageID");
        EzXml::setNodeText(pMessageID,"uuid:"+m_uuid);
        TiXmlElement* pRelatesTo = EzXml::getChildElement(&doc,"Envelope","Header","RelatesTo");
        EzXml::setNodeText(pRelatesTo,"uuid:"+messageId);
        TiXmlElement* pAddress = EzXml::getChildElement(&doc,"Envelope","Body","ProbeMatches","ProbeMatch","EndpointReference","Address");
        EzXml::setNodeText(pAddress,"urn:uuid:"+m_uuid);
        TiXmlElement* pXAddrs = EzXml::getChildElement(&doc,"Envelope","Body","ProbeMatches","ProbeMatch","XAddrs");
        EzXml::setNodeText(pXAddrs,"http://"+Ez::EzOnvifServer::getLocalIP()+":"+ABMUtils::itoa(m_port)+m_path);
        res = EzXml::getStreamXmlString(&doc);
    }
    return res;
}

std::string EzOnvifServer::onMulticastData(std::string data)
{
    std::string res = "";
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(data.c_str());
    if(!doc.Error()){
        TiXmlElement* pMessageID = EzXml::getChildElement(&doc,"Envelope","Header","MessageID");
        std::string messageId = EzXml::getValueWithoutXmlns(EzXml::getNodeText(pMessageID));
        TiXmlElement* pTypes = EzXml::getChildElement(&doc,"Envelope","Body","Probe","Types");
        std::string type = EzXml::getValueWithoutXmlns(EzXml::getNodeText(pTypes));
        res = getResProbeMatches(messageId,type);
    }
    return res;
}

std::string EzOnvifServer::getPasswordDigest(std::string password,std::string nonce,std::string created){
    nonce = base64Decode(nonce);
    const EVP_MD *md;
    EVP_MD_CTX mdctx;
    unsigned char md_value[EVP_MAX_MD_SIZE];
    unsigned int md_len;
    OpenSSL_add_all_digests();
    md = EVP_get_digestbyname("sha1");
    EVP_MD_CTX_init(&mdctx);
    EVP_DigestInit_ex(&mdctx, md, NULL);
    EVP_DigestUpdate(&mdctx, nonce.c_str(), nonce.size());
    EVP_DigestUpdate(&mdctx, created.c_str(), created.size());
    EVP_DigestUpdate(&mdctx, password.c_str(),password.size());
    EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
    EVP_MD_CTX_cleanup(&mdctx);
    std::string sha1 = std::string((char*)md_value,md_len);
    std::string ret = base64Encode(sha1);
//    printf("PasswordDigest:%s\n",ret.c_str());
    return ret;
}

bool EzOnvifServer::isCheckAuthorized(std::string cmd){
    bool ret = true;
    if(cmd=="GetDeviceInformation" ||cmd=="GetCapabilities"||cmd=="GetSystemDateAndTime") ret = false;
    return ret;
}

bool EzOnvifServer::checkUsernameToken(TiXmlElement* usernameToken)
{
    bool ret = false;
    if(usernameToken){
        TiXmlElement* pUsername = EzXml::getChildElement(usernameToken,"Username");
        std::string username = EzXml::getValueWithoutXmlns(EzXml::getNodeText(pUsername));
        TiXmlElement* pPassword = EzXml::getChildElement(usernameToken,"Password");
        std::string password = EzXml::getValueWithoutXmlns(EzXml::getNodeText(pPassword));
        printf("PaaswordSha1:%s\n",EzText::StringToHexString(base64Decode(password)).c_str());
        std::string passwordType = pPassword->Attribute("Type");
        TiXmlElement* pNonce = EzXml::getChildElement(usernameToken,"Nonce");
        std::string nonce = EzXml::getValueWithoutXmlns(EzXml::getNodeText(pNonce));
        TiXmlElement* pCreated = EzXml::getChildElement(usernameToken,"Created");
        std::string created = EzXml::getNodeText(pCreated);
        std::string passwordDigest = getPasswordDigest(AccountData::getInstance()->getCameraInfo().strPassword,nonce,created);
        printf("PaaswordSha2:%s\n",EzText::StringToHexString(base64Decode(passwordDigest)).c_str());
        printf("strUserName:%s\n",AccountData::getInstance()->getCameraInfo().strUserName.c_str());
        printf("username:%s\n",username.c_str());
        if(AccountData::getInstance()->getCameraInfo().strUserName==username&&passwordDigest==password) ret = true;
    }
    return ret;
}

std::string EzOnvifServer::getHttpHead(int code,long len)
{
    std::string phrase = "Unknow";
    switch (code) {
        case 200:
            phrase = "OK";
            break;
        case 400:
            phrase = "Bad Request";
            break;
        case 500:
            phrase = "Internal Server Error";
            break;
        default:
            break;
    }
    std::string res = "";
    res = res + "HTTP/1.1 " + ABMUtils::itoa(code) + " " + phrase + "\r\n";
    res = res + "Cache-Control: public,max-age=0\r\n";
    res = res + "Server: gSOAP/2.8\r\n";
    res = res + "Content-Type: application/soap+xml; charset=utf-8\r\n";
    res = res + "Content-Length: " + ABMUtils::itoa(len) +"\r\n";
    res = res + "Connection: close\r\n";
    res = res + "Date: " + Ez::getGMTTimestmapString("%a, %d %b %Y %T GMT") + "\r\n";//Thu, 09 Aug 2018 10:20:52 GMT
    res = res + "\r\n";
    return res;
}

std::string EzOnvifServer::getResInvalidArgVal(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_FaultInvalidArgVal);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(400,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResNotAuthorized(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_FaultNotAuthorized);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(400,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetDeviceInformation(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetDeviceInformationResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pManufacturer = EzXml::getChildElement(&doc,"Envelope","Body","GetDeviceInformationResponse","Manufacturer");
        EzXml::setNodeText(pManufacturer,"ezhomelabs");
        TiXmlElement* pModel = EzXml::getChildElement(&doc,"Envelope","Body","GetDeviceInformationResponse","Model");
        EzXml::setNodeText(pModel,AccountData::getInstance()->getSerialType());
        TiXmlElement* pFirmwareVersion = EzXml::getChildElement(&doc,"Envelope","Body","GetDeviceInformationResponse","FirmwareVersion");
        EzXml::setNodeText(pFirmwareVersion,AccountData::getInstance()->getVersion());
        TiXmlElement* pSerialNumber = EzXml::getChildElement(&doc,"Envelope","Body","GetDeviceInformationResponse","SerialNumber");
        EzXml::setNodeText(pSerialNumber,AccountData::getInstance()->getSerialNumber());
        TiXmlElement* pHardwareId = EzXml::getChildElement(&doc,"Envelope","Body","GetDeviceInformationResponse","HardwareId");
        EzXml::setNodeText(pHardwareId,AccountData::getInstance()->getMACAddr());
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}
std::string EzOnvifServer::getResGetCapabilities(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetCapabilitiesResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        std::string strIP = Ez::EzOnvifServer::getLocalIP();
        TiXmlElement* pAnalyticsXAddr = EzXml::getChildElement(&doc,"Envelope","Body","GetCapabilitiesResponse","Capabilities","Analytics","XAddr");
        EzXml::setNodeText(pAnalyticsXAddr,"http://"+strIP+":"+ABMUtils::itoa(m_port)+m_path);
        TiXmlElement* pDeviceXAddr = EzXml::getChildElement(&doc,"Envelope","Body","GetCapabilitiesResponse","Capabilities","Device","XAddr");
        EzXml::setNodeText(pDeviceXAddr,"http://"+strIP+":"+ABMUtils::itoa(m_port)+m_path);
        TiXmlElement* pEventsXAddr = EzXml::getChildElement(&doc,"Envelope","Body","GetCapabilitiesResponse","Capabilities","Events","XAddr");
        EzXml::setNodeText(pEventsXAddr,"http://"+strIP+":"+ABMUtils::itoa(m_port)+m_path);
        TiXmlElement* pImagingXAddr = EzXml::getChildElement(&doc,"Envelope","Body","GetCapabilitiesResponse","Capabilities","Imaging","XAddr");
        EzXml::setNodeText(pImagingXAddr,"http://"+strIP+":"+ABMUtils::itoa(m_port)+m_path);
        TiXmlElement* pMediaXAddr = EzXml::getChildElement(&doc,"Envelope","Body","GetCapabilitiesResponse","Capabilities","Media","XAddr");
        EzXml::setNodeText(pMediaXAddr,"http://"+strIP+":"+ABMUtils::itoa(m_port)+m_path);
        TiXmlElement* pPTZXAddr = EzXml::getChildElement(&doc,"Envelope","Body","GetCapabilitiesResponse","Capabilities","PTZ","XAddr");
        EzXml::setNodeText(pPTZXAddr,"http://"+strIP+":"+ABMUtils::itoa(m_port)+m_path);
        TiXmlElement* pDeviceIOXAddr = EzXml::getChildElement(&doc,"Envelope","Body","GetCapabilitiesResponse","Capabilities","Extension","DeviceIO","XAddr");
        EzXml::setNodeText(pDeviceIOXAddr,"http://"+strIP+":"+ABMUtils::itoa(m_port)+m_path);
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}
void EzOnvifServer::addVideoEncoderConfiguration(TiXmlElement* parent,std::string rootValue)
{
    std::string resTemplate =Ez::base64Decode(FILE_Part_VideoEncoderConfiguration);
    TiXmlElement* pVideoEncoderConfiguration = EzXml::newPointer_getRootElementFormXmlString(resTemplate);
    if(!rootValue.empty()) pVideoEncoderConfiguration->SetValue(rootValue.c_str());
    EzXml::setElementAttribute(pVideoEncoderConfiguration,"token","H264Video_cfg_token1");
    TiXmlElement* pName = EzXml::getChildElement(pVideoEncoderConfiguration,"Name");
    EzXml::setNodeText(pName,"H264Video_cfg1");
    TiXmlElement* pEncoding = EzXml::getChildElement(pVideoEncoderConfiguration,"Encoding");
    EzXml::setNodeText(pEncoding,"H264");
    TiXmlElement* pResolutionWidth = EzXml::getChildElement(pVideoEncoderConfiguration,"Resolution","Width");
    EzXml::setNodeText(pResolutionWidth,ABMUtils::itoa(AccountData::getInstance()->getMAXWidth()));
    TiXmlElement* pResolutionHeight = EzXml::getChildElement(pVideoEncoderConfiguration,"Resolution","Height");
    EzXml::setNodeText(pResolutionHeight,ABMUtils::itoa(AccountData::getInstance()->getMAXHeight()));
    parent->LinkEndChild(pVideoEncoderConfiguration);
}

void EzOnvifServer::addAudioEncoderConfiguration(TiXmlElement* parent,std::string rootValue)
{
    std::string resTemplate =Ez::base64Decode(FILE_Part_AudioEncoderConfiguration);
    TiXmlElement* pAudioEncoderConfiguration = EzXml::newPointer_getRootElementFormXmlString(resTemplate);
    if(!rootValue.empty()) pAudioEncoderConfiguration->SetValue(rootValue.c_str());
    EzXml::setElementAttribute(pAudioEncoderConfiguration,"token","AACAudio_cfg_token1");
    TiXmlElement* pName = EzXml::getChildElement(pAudioEncoderConfiguration,"Name");
    EzXml::setNodeText(pName,"AACAudio_cfg");
    TiXmlElement* pEncoding = EzXml::getChildElement(pAudioEncoderConfiguration,"Encoding");
    EzXml::setNodeText(pEncoding,"AAC");
    TiXmlElement* pBitrate = EzXml::getChildElement(pAudioEncoderConfiguration,"Bitrate");
    EzXml::setNodeText(pBitrate,ABMUtils::itoa(16));
    TiXmlElement* pSampleRate = EzXml::getChildElement(pAudioEncoderConfiguration,"SampleRate");
    EzXml::setNodeText(pSampleRate,ABMUtils::itoa(8000));
    parent->LinkEndChild(pAudioEncoderConfiguration);
}
void  EzOnvifServer::addProfile(TiXmlElement* parent,Profile profile,std::string rootValue)
{
    std::string resTemplate =Ez::base64Decode(FILE_Part_Profiles);
    TiXmlElement* pProfile = EzXml::newPointer_getRootElementFormXmlString(resTemplate);
    if(!rootValue.empty()) pProfile->SetValue(rootValue.c_str());
    EzXml::setElementAttribute(pProfile,"token",profile.token);
    TiXmlElement* pName = EzXml::getChildElement(pProfile,"Name");
    EzXml::setNodeText(pName,profile.Name);
    TiXmlElement* pBouds = EzXml::getChildElement(pProfile,"VideoSourceConfiguration","Bounds");
    EzXml::setElementAttribute(pBouds,"width",ABMUtils::itoa(AccountData::getInstance()->getMAXWidth()));
    EzXml::setElementAttribute(pBouds,"height",ABMUtils::itoa(AccountData::getInstance()->getMAXHeight()));
    
    TiXmlElement* pVideoEncoderConfiguration = EzXml::getChildElement(pProfile,"VideoEncoderConfiguration");
    pProfile->RemoveChild(pVideoEncoderConfiguration);
    addVideoEncoderConfiguration(pProfile);
    TiXmlElement* pAudioEncoderConfiguration = EzXml::getChildElement(pProfile,"AudioEncoderConfiguration");
    pProfile->RemoveChild(pAudioEncoderConfiguration);
    addAudioEncoderConfiguration(pProfile);
    parent->LinkEndChild(pProfile);
}

void EzOnvifServer::addVideoSource(TiXmlElement* parent)
{
    std::string resTemplate =Ez::base64Decode(FILE_Part_VideoSources);
    TiXmlElement* pVideoSources = EzXml::newPointer_getRootElementFormXmlString(resTemplate);
    EzXml::setElementAttribute(pVideoSources,"token","VideoSource_token1");
    TiXmlElement* pFramerate = EzXml::getChildElement(pVideoSources,"Framerate");
    EzXml::setNodeText(pFramerate,ABMUtils::itoa(25));
    TiXmlElement* pResolutionWidth = EzXml::getChildElement(pVideoSources,"Resolution","Width");
    EzXml::setNodeText(pResolutionWidth,ABMUtils::itoa(AccountData::getInstance()->getMAXWidth()));
    TiXmlElement* pResolutionHeight = EzXml::getChildElement(pVideoSources,"Resolution","Height");
    EzXml::setNodeText(pResolutionHeight,ABMUtils::itoa(AccountData::getInstance()->getMAXHeight()));
    parent->LinkEndChild(pVideoSources);
}

std::string EzOnvifServer::getResGetProfiles(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetProfilesResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pProfiles = EzXml::getChildElement(&doc,"Envelope","Body","GetProfilesResponse");
        pProfiles->Clear();
        for (int i = 0 ; i < m_profileList.size(); i++) {
            Profile profile = m_profileList.at(i);
            addProfile(pProfiles, profile);
        }
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetProfile(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetProfileResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pProfileToken = EzXml::getChildElement(pDoc,"Envelope","Body","GetProfile","ProfileToken");
        std::string profileToken = EzXml::getNodeText(pProfileToken);
        Profile profile = getProfileFromToken(profileToken);
        if(profileToken.empty()||profile.token.empty()){
            return getResInvalidArgVal(pDoc);
        }
        
        TiXmlElement* pProfile = EzXml::getChildElement(&doc,"Envelope","Body","GetProfileResponse");
        pProfile->Clear();
        addProfile(pProfile, profile,"trt:Profile");
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

EzOnvifServer::Profile EzOnvifServer::getProfileFromToken(std::string token)
{
    Profile ret;
    ret.token.clear();
    for (int i = 0 ; i < m_profileList.size(); i ++) {
        if(m_profileList.at(i).token == token){
            return m_profileList.at(i);
        }
    }
    return ret;
}

std::string EzOnvifServer::getResGetStreamUri(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string uri = "";
    
    TiXmlElement* pProfileToken = EzXml::getChildElement(pDoc,"Envelope","Body","GetStreamUri","ProfileToken");
    std::string profileToken = EzXml::getNodeText(pProfileToken);
    Profile profile = getProfileFromToken(profileToken);
    if(profileToken.empty()||profile.token.empty()){
        return getResInvalidArgVal(pDoc);
    }else{
//        if(profile.streamUri.host.empty())
        profile.streamUri.host = Ez::EzOnvifServer::getLocalIP();
        uri = profile.streamUri.protocol + "://"+AccountData::getInstance()->getCameraInfo().strUserName+":"+AccountData::getInstance()->getCameraInfo().strPassword+"@" + profile.streamUri.host + ":" + ABMUtils::itoa(profile.streamUri.port) + profile.streamUri.path;
//        uri = profile.streamUri.protocol + "://192.168.3.2:554" + profile.streamUri.path;
    }
    
    std::string resTemplate =Ez::base64Decode(FILE_GetStreamUriResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pUri = EzXml::getChildElement(&doc,"Envelope","Body","GetStreamUriResponse","MediaUri","Uri");
        EzXml::setNodeText(pUri,uri);
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetSnapshotUri(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string uri = "";
    
    TiXmlElement* pProfileToken = EzXml::getChildElement(pDoc,"Envelope","Body","GetSnapshotUri","ProfileToken");
    std::string profileToken = EzXml::getNodeText(pProfileToken);
    Profile profile = getProfileFromToken(profileToken);
    if(profileToken.empty()||profile.token.empty()){
        return getResInvalidArgVal(pDoc);
    }else{
//        if(profile.snapshotUri.host.empty())
        profile.snapshotUri.host = Ez::EzOnvifServer::getLocalIP();
        uri = profile.snapshotUri.protocol + "://"+AccountData::getInstance()->getCameraInfo().strUserName+":"+AccountData::getInstance()->getCameraInfo().strPassword+"@" + profile.snapshotUri.host + ":" + ABMUtils::itoa(profile.snapshotUri.port) + profile.snapshotUri.path;
    }
    
    std::string resTemplate =Ez::base64Decode(FILE_GetSnapshotUriResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pUri = EzXml::getChildElement(&doc,"Envelope","Body","GetSnapshotUriResponse","MediaUri","Uri");
        EzXml::setNodeText(pUri,uri);
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetVideoSourceConfiguration(TiXmlDocument* pDoc)
{
//    TiXmlElement* pSecurity = EzXml::getChildElement(pDoc,"Envelope","Header","Security");
//    if(!pSecurity){
//        return getResInvalidArgVal(pDoc);
//    }
    
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetVideoSourceConfigurationResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pHeader = EzXml::getChildElement(&doc,"Envelope","Header");
        pHeader->Clear();
//        EzXml::LinkEndChildWithClone(pHeader, pSecurity);
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetVideoEncoderConfigurations(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetVideoEncoderConfigurationsResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pEncoderConfigurations = EzXml::getChildElement(&doc,"Envelope","Body","GetVideoEncoderConfigurationsResponse");
        pEncoderConfigurations->Clear();
        for (int i = 0 ; i < m_profileList.size(); i++) {
            Profile profile = m_profileList.at(i);
            addVideoEncoderConfiguration(pEncoderConfigurations,"trt:Configurations");
        }
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetServices(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetServicesResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pGetServicesResponse = EzXml::getChildElement(&doc,"Envelope","Body","GetServicesResponse");
        for (TiXmlElement* element = pGetServicesResponse->FirstChildElement(); element!=NULL; element=element->NextSiblingElement()) {
            TiXmlElement* pXAddr = EzXml::getChildElement(&doc,"XAddr");
            EzXml::setNodeText(pXAddr,"http://"+Ez::EzOnvifServer::getLocalIP()+":"+ABMUtils::itoa(m_port)+m_path);
        }
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetSystemDateAndTime(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetSystemDateAndTimeResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pUTCHour = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","UTCDateTime","Time","Hour");
        EzXml::setNodeText(pUTCHour, getGMTTimestmapString("%H"));
        TiXmlElement* pUTCMinute = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","UTCDateTime","Time","Minute");
        EzXml::setNodeText(pUTCMinute, getGMTTimestmapString("%M"));
        TiXmlElement* pUTCSecond = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","UTCDateTime","Time","Second");
        EzXml::setNodeText(pUTCSecond, getGMTTimestmapString("%S"));
        TiXmlElement* pUTCYear = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","UTCDateTime","Date","Year");
        EzXml::setNodeText(pUTCYear, getGMTTimestmapString("%Y"));
        TiXmlElement* pUTCMonth = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","UTCDateTime","Date","Month");
        EzXml::setNodeText(pUTCMonth, getGMTTimestmapString("%m"));
        TiXmlElement* pUTCDay = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","UTCDateTime","Date","Day");
        EzXml::setNodeText(pUTCDay, getGMTTimestmapString("%d"));
        TiXmlElement* pLocalHour = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","LocalDateTime","Time","Hour");
        EzXml::setNodeText(pLocalHour, getTimestmapString("%H"));
        TiXmlElement* pLocalMinute = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","LocalDateTime","Time","Minute");
        EzXml::setNodeText(pLocalMinute, getTimestmapString("%M"));
        TiXmlElement* pLocalSecond = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","LocalDateTime","Time","Second");
        EzXml::setNodeText(pLocalSecond, getTimestmapString("%S"));
        TiXmlElement* pLocalYear = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","LocalDateTime","Date","Year");
        EzXml::setNodeText(pLocalYear, getTimestmapString("%Y"));
        TiXmlElement* pLocalMonth = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","LocalDateTime","Date","Month");
        EzXml::setNodeText(pLocalMonth, getTimestmapString("%m"));
        TiXmlElement* pLocalDay = EzXml::getChildElement(&doc,"Envelope","Body","GetSystemDateAndTimeResponse","SystemDateAndTime","LocalDateTime","Date","Day");
        EzXml::setNodeText(pLocalDay, getTimestmapString("%d"));
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResGetVideoSources(TiXmlDocument* pDoc)
{
    std::string res = "";
    std::string resTemplate =Ez::base64Decode(FILE_GetVideoSourcesResponse);
    TiXmlDocument doc = TiXmlDocument();
    doc.Parse(resTemplate.c_str());
    if(!doc.Error()){
        TiXmlElement* pVideoSources = EzXml::getChildElement(&doc,"Envelope","Body","GetVideoSourcesResponse");
        pVideoSources->Clear();
        for (int i = 0 ; i < m_profileList.size(); i++) {
            addVideoSource(pVideoSources);
        }
        res = EzXml::getStreamXmlString(&doc);
        res = EzOnvifServer::getHttpHead(200,res.size()) + res;
    }
    return res;
}

std::string EzOnvifServer::getResFromCMD(TiXmlDocument* pDoc,std::string cmd)
{
    std::string res = "";
    if (cmd == "GetDeviceInformation") {
        res = getResGetDeviceInformation(pDoc);
    }else if(cmd == "GetCapabilities"){
        res = getResGetCapabilities(pDoc);
    }else if(cmd == "GetSystemDateAndTime"){
        res = getResGetSystemDateAndTime(pDoc);
    }else if(cmd == "GetProfiles"){
        res = getResGetProfiles(pDoc);
    }else if(cmd == "GetProfile"){
        res = getResGetProfile(pDoc);
    }else if(cmd == "GetStreamUri"){
        res = getResGetStreamUri(pDoc);
    }else if(cmd == "GetSnapshotUri"){
        res = getResGetSnapshotUri(pDoc);
    }else if(cmd == "GetVideoEncoderConfigurations"){
        res = getResGetVideoEncoderConfigurations(pDoc);
    }else if(cmd == "GetServices"){
        res = getResGetServices(pDoc);
    }else if(cmd == "GetVideoSources"){
        res = getResGetVideoSources(pDoc);
    }else if(cmd == "GetVideoSourceConfiguration"){
        res = getResGetVideoSourceConfiguration(pDoc);
    }else{
        res = getResInvalidArgVal(pDoc);
    }
    return res;
}

std::string EzOnvifServer::onSocketData(std::string data)
{
    if(!m_lastBuffer.empty()) data = m_lastBuffer + data;
    std::string res = "";
    size_t startLen = 0;
    size_t start_len = std::string::npos;
    while (startLen != std::string::npos) {
        startLen = data.find("Content-Length: ",startLen+16);
        if(startLen!=std::string::npos){
            start_len = startLen;
//            printf("start_len:%d\n",start_len);
        }
    }
//    printf("The Content-Length POS:%d\n",start_len);
    
    if(start_len == std::string::npos){
        m_lastBuffer.clear();
        return "";
    }
    size_t end_len = data.find("\r\n",start_len);
    if(start_len == std::string::npos){
        m_lastBuffer = data;
        return "";
    }
    int contentLength = atoi(data.substr(start_len+16,end_len-start_len-16).c_str());
    size_t pos = data.find("\r\n\r\n");
    if(pos != std::string::npos){
        std::string onvifxml = data.substr(pos+4,contentLength);
        if(onvifxml.size()!=contentLength){
            printf("RecvLength:%d/%d\n",onvifxml.size(),contentLength);
            m_lastBuffer = data;
            return "";
        }else{
            m_lastBuffer = data.substr(pos+4+contentLength);
        }
        TiXmlDocument doc = TiXmlDocument();
        doc.Parse(onvifxml.c_str());
        if(!doc.Error()){
            TiXmlElement* pBody = EzXml::getChildElement(&doc,"Envelope","Body");
            std::string cmd = "";
            if(pBody&&pBody->FirstChildElement())  cmd = EzXml::getValueWithoutXmlns(pBody->FirstChildElement()->Value());
            printf("#################### cmd:>>>%s<<<\n",cmd.c_str());
            if(isCheckAuthorized(cmd)){
                if(checkUsernameToken(EzXml::getChildElement(&doc,"Envelope","Header","Security","UsernameToken"))){
                    res = getResFromCMD(&doc,cmd);
                }else{
                    res = getResNotAuthorized(&doc);
                }
            }else{
                res = getResFromCMD(&doc,cmd);
            }
        }
    }else{
        m_lastBuffer = data;
        return "";
    }
    return res;
}

EZ_END
